
#pragma once

#include "glm\glm.hpp"
#include "glm\ext.hpp"
using namespace glm;


class Camera
{
public:
	Camera()
	{
		_position = vec3(0.0f, 0.0f, 0.0f);
        _rotation = vec3(0.0f, 0.0f, 0.0f);
        
        _xLocal = vec3(1.0f, 0.0f, 0.0f);
        _yLocal = vec3(0.0f, 1.0f, 0.0f);
        _zLocal = vec3(0.0f, 0.0f, 1.0f);
        
        _vMatrix[0][0] = _xLocal.x;    _vMatrix[0][1] = _yLocal.x;    _vMatrix[0][2] = _zLocal.x;    _vMatrix[0][3] = 0.0f;
        _vMatrix[1][0] = _xLocal.y;    _vMatrix[1][1] = _yLocal.y;    _vMatrix[1][2] = _zLocal.y;    _vMatrix[1][3] = 0.0f;
        _vMatrix[2][0] = _xLocal.z;    _vMatrix[2][1] = _yLocal.z;    _vMatrix[2][2] = _zLocal.z;    _vMatrix[2][3] = 0.0f;
        _vMatrix[3][0] = - dot(_position, _xLocal );
        _vMatrix[3][1] = - dot(_position, _yLocal);
        _vMatrix[3][2] = - dot(_position, _zLocal);
        _vMatrix[3][3] = 1.0f;
	}
	~Camera() {}

	mat4 getViewMatrix()
	{
		return _vMatrix;
	}

	void update()
	{
		_xLocal = vec3(1.0f, 0.0f, 0.0f);
        _yLocal = vec3(0.0f, 1.0f, 0.0f);
        _zLocal = vec3(0.0f, 0.0f, 1.0f);

		glm::mat4 buf = glm::rotate(glm::mat4(1.0f), _rotation.z, _xLocal);
		buf = glm::rotate(buf, _rotation.y, _yLocal);
		buf = glm::rotate(buf, _rotation.x, _xLocal);


		vec3 col1(buf[0][0], buf[1][0], buf[2][0]);
		vec3 col2(buf[0][1], buf[1][1], buf[2][1]);
		vec3 col3(buf[0][2], buf[1][2], buf[2][2]);
		_xLocal = vec3(dot(col1, _xLocal), dot(col2, _xLocal), dot(col3, _xLocal));
		_yLocal = vec3(dot(col1, _yLocal), dot(col2, _yLocal), dot(col3, _yLocal));
		_zLocal = vec3(dot(col1, _zLocal), dot(col2, _zLocal), dot(col3, _zLocal));

		_vMatrix[0][0] = _xLocal.x;    _vMatrix[0][1] = _yLocal.x;    _vMatrix[0][2] = _zLocal.x;    _vMatrix[0][3] = 0.0f;
        _vMatrix[1][0] = _xLocal.y;    _vMatrix[1][1] = _yLocal.y;    _vMatrix[1][2] = _zLocal.y;    _vMatrix[1][3] = 0.0f;
        _vMatrix[2][0] = _xLocal.z;    _vMatrix[2][1] = _yLocal.z;    _vMatrix[2][2] = _zLocal.z;    _vMatrix[2][3] = 0.0f;
        _vMatrix[3][0] = - dot(_position, _xLocal );
        _vMatrix[3][1] = - dot(_position, _yLocal);
        _vMatrix[3][2] = - dot(_position, _zLocal);
        _vMatrix[3][3] = 1.0f;
	}

	void translate(vec3 dv)
	{
		_position += _xLocal * dv.x;
		_position += _yLocal * dv.y;
		_position += _zLocal * dv.z;
	}

	void rotate(vec3 da)
	{
		_rotation += da;
	}

public:
	mat4 _vMatrix;
	vec3 _position;
	vec3 _rotation;

	vec3 _xLocal;
	vec3 _yLocal;
	vec3 _zLocal;
};


class CameraController
{
public:
	CameraController(Camera* camera)
	{
		assert(camera != nullptr);

		_camera = camera;
	}

	void onTouchDown(float x, float y)
	{
		_touched = true;
		_touchPos = vec2(x, y);
	}
	void onTouchMove(float x, float y)
	{
		if (_touched && _camera != nullptr) {
			vec2 dv = vec2(x, y) - _touchPos;
			_camera->rotate(vec3(-dv.y, -dv.x, 0.0) * 0.1f);
		}

		_touchPos = vec2(x, y);
	}
	void onTouchUp(float x, float y)
	{
		_touched = false;
	}

private:
	Camera* _camera;
	bool _touched;
	vec2 _touchPos;
};